#pragma once

#include <iostream>
#include <memory>
#include "Socket.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include <string>

using namespace socket_ns;

class SelectServer
{
    const static int N = sizeof(fd_set) * 8; // 结构体*8=最多表示多少比特位
    const static int defaultfd = -1;

public:
    SelectServer(uint16_t port) : _port(port), _listensock(std::make_unique<TcpSocket>())
    {
        InetAddr addr("0", _port);
        _listensock->BuildListenSocket(addr); // 创建套接字 + 绑定 + 监听 + 重用端口
        for (int i = 0; i < N; i++)
        {
            _fd_array[i] = defaultfd; // 初始化
        }
        _fd_array[0] = _listensock->SockFd(); // 将listensock的fd放到数组中
    }

    ~SelectServer()
    {
    }

    void AcceptClient()
    {

        InetAddr clientaddr;
        int sockfd = _listensock->Accepter(&clientaddr); // 建立连接
        if (sockfd < 0)
            return;

        LOG(DEBUG, "Get new sockfd: %d,client info %s:%d\n", sockfd, clientaddr.IP().c_str(), clientaddr.Port()); // 打印

        int pos = 1;
        for (; pos < N; pos++)
        {
            if (_fd_array[pos] == defaultfd)
                break; // 找到第一个空位
        }
        if (pos == N)
        {
            ::close(sockfd); // 没有位置了，关闭连接
            LOG(WARNING, "server is full!\n");
            return;
        }
        else
        {
            _fd_array[pos] = sockfd; // 将新连接的fd放到数组中
            LOG(DEBUG, "%d add to select array!\n", sockfd);
        }
        LOG(DEBUG, "curr fd_array[] fd list: %s\n", Rfdtostring().c_str()); // 打印当前数组中fd列表
    }
    void ServiceIO(int pos)
    {
        char buffer[1024];
        ssize_t n = ::recv(_fd_array[pos], buffer, sizeof(buffer) - 1, 0); // 接收数据
        if (n > 0)
        {
            buffer[n] = 0;
            std::cout << "client say# " << buffer << std::endl;
            std::string echo_string = "[server echo]# ";
            echo_string += buffer;
            ::send(_fd_array[pos], echo_string.c_str(), echo_string.size(), 0); // 发送数据
        }
        else if (n == 0)
        {
            LOG(DEBUG, "%d is close\n", _fd_array[pos]);
            ::close(_fd_array[pos]);                                            // 关闭连接
            _fd_array[pos] = defaultfd;                                         // 将数组中该位置置为空位
            LOG(DEBUG, "curr fd_array[] fd list: %s\n", Rfdtostring().c_str()); // 打印当前数组中fd列表
        }
        else
        {
            LOG(DEBUG, "%d recv error\n", _fd_array[pos]);
            ::close(_fd_array[pos]); // 关闭连接
            _fd_array[pos] = defaultfd;
            LOG(DEBUG, "curr fd_array[] fd list: %s\n", Rfdtostring().c_str()); // 打印当前数组中fd列表
        }
    }

    void HandlerEvent(fd_set &rfds)
    {
        for (int i = 0; i < N; i++)
        {
            if (_fd_array[i] == defaultfd) // 空位
                continue;
            if (FD_ISSET(_fd_array[i], &rfds)) // 套接字是否就绪
            {
                if (_fd_array[i] == _listensock->SockFd()) // 如果是listensock
                {
                    AcceptClient(); // 接受新连接
                }
                else
                {
                    ServiceIO(i); // 处理客户端请求
                }
            }
        }
    }

    void Loop()
    {
        while (1)
        {
            // listensocket 等待新连接到来，等价于对方给我发送数据！我们作为读事件同一处理
            // 新连接到来 等价于 读事件就绪！
            // 首先要将listensock添加到select中！
            fd_set rfds;            // 读事件集合
            FD_ZERO(&rfds);         // 清空读集合
            int max_fd = defaultfd; // 最大fd

            for (int i = 0; i < N; i++)
            {
                if (_fd_array[i] == defaultfd)
                    continue;
                FD_SET(_fd_array[i], &rfds); // 将合法的套接字fd添加到集合中
                if (max_fd < _fd_array[i])
                {
                    max_fd = _fd_array[i]; // 找到最大的fd
                }
            }

            struct timeval timeout = {0, 0}; // 超时时间 1s

            int n = select(max_fd + 1, &rfds, NULL, NULL, /*&timeout*/ nullptr); // 等待读事件 nullptr阻塞等待
            switch (n)
            {
            case 0: // 超时
                LOG(INFO, "timeout, %d.%d\n", timeout.tv_sec, timeout.tv_usec);
                break;
            case -1: // 错误
                LOG(ERROR, "select error...\n");
                break;
            default: // 有事件发生
                LOG(DEBUG, "Event Happen!, n:%d \n", n);
                HandlerEvent(rfds);
                break;
            }
        }
    }

    std::string Rfdtostring()
    {
        std::string fdstr;
        for (int i = 0; i < N; i++)
        {
            if (_fd_array[i] == defaultfd)
                continue;
            fdstr += std::to_string(_fd_array[i]);
            fdstr += " ";
        }
        return fdstr;
    }

private:
    uint16_t _port;
    std::unique_ptr<Socket> _listensock;

    int _fd_array[N]; // 存放所有客户端的fd
};